package cpr;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import cpr.context.CacheRuleContext;
import cpr.context.Context;
import cpr.context.ruleacceptor.IRuleAcceptor;
import cpr.context.ruleacceptor.IRuleAcceptorCache;


public class IOContext {
    Context ctx;
    public IOContext(Context ctx) {
        this.ctx = ctx;
    }

    public static int constructBytes(int value, byte[] bytes, int byteSizeOf, int offset) {
        switch (byteSizeOf) {
        case 1:
            bytes[offset+0] = (byte)((value >>>  0) & 0xFF);
            break;
        case 2:
            bytes[offset+0] = (byte)((value >>>  8) & 0xFF);
            bytes[offset+1] = (byte)((value >>>  0) & 0xFF);
            break;
        case 3:
            bytes[offset+0] = (byte)((value >>> 16) & 0xFF);
            bytes[offset+1] = (byte)((value >>>  8) & 0xFF);
            bytes[offset+2] = (byte)((value >>>  0) & 0xFF);
            break;
        case 4:
            bytes[offset+0] = (byte)((value >>> 24) & 0xFF);
            bytes[offset+1] = (byte)((value >>> 16) & 0xFF);
            bytes[offset+2] = (byte)((value >>>  8) & 0xFF);
            bytes[offset+3] = (byte)((value >>>  0) & 0xFF);
            break;
        default:
            throw new RuntimeException("not supported");
        }
        return byteSizeOf;
    }
    
    private static int constructInt(int[] bytes) {
        int result = 0;
        switch(bytes.length) {
        case 1:
            result = bytes[0];
            break;
        case 2:
            result = (bytes[0] << 8) + (bytes[1] << 0);
            break;
        case 3:
            result = (bytes[0] << 16) + (bytes[1] << 8) + (bytes[2] << 0);
            break;
        case 4:
            result = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + (bytes[3] << 0);
            break;
        default:
            throw new RuntimeException("not supported");
        }
        return result;
    }
    
    private static char readReliable(InputStream _is) throws IOException {
        int val = -1;
        int sleeps = 0;
        while (val==-1){
            val = _is.read();
            if (val>=0)return (char)val;
            if (sleeps>1000) throw new RuntimeException("Timout reading a socket");
            try {
                Thread.currentThread().sleep(20);
                sleeps++;
            } catch (InterruptedException e) {  
            }
        }
        //unreachable
        return 0;
    }

    
    public byte[] convertRuleToBytes(Rule aRule) {
        int reqNumber = Rule.HL_SIZE+1;
        int reqSizeOf = getRuleRecordByteSize();
        int headerSize = 5;
        byte[] result = new byte[headerSize+reqNumber*reqSizeOf];
        int pointer = 0;
        pointer +=constructBytes(reqNumber, result, 4, pointer);
        pointer +=constructBytes(reqSizeOf, result, 1, pointer);
        for (int i = 0; i < Rule.HL_SIZE; i++) {
            pointer +=constructBytes(aRule.hl_digital[i], result, reqSizeOf, pointer);
        }
        pointer +=constructBytes(aRule.attrValueDigital, result, reqSizeOf, pointer);
        return result;
        
    }
    public Rule readRuleFromPureDigital(InputStream is) { //, int ruleNr, int ruleNrGlobal
        try {
            if (is.available()==0) {
                return null;
            }
            Rule aRule = readPureDigitalRule(is);
//            if (ruleNr>=0) {
//                aRule.ruleNr            = ruleNr;
//            }
//            if (ruleNrGlobal>=0) {
//                aRule.ruleNrGlobal      = ruleNrGlobal;
//            }
            return aRule;
        } catch (Exception ex) {
            throw new RuntimeException();
        }
    }
    
    public boolean scrollToNextRule(InputStream is) throws Exception {
    	if (is.available()==0) {
            return false;
        }
    	int ch1 = readReliable(is);
        int ch2 = readReliable(is);
        int ch3 = readReliable(is);
        int ch4 = readReliable(is);
        int reqNumber =((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
        int reqSizeOf = readReliable(is);
        int[] record = new int[reqNumber*reqSizeOf];
        for (int i=0; i<record.length; i++){
            record[i]=readReliable(is);
        }
        return true;
    }
    
    public Rule readPureDigitalRule(InputStream _is) throws Exception {
        int ch1 = readReliable(_is);
        int ch2 = readReliable(_is);
        int ch3 = readReliable(_is);
        int ch4 = readReliable(_is);
        int reqNumber =((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
        int reqSizeOf = readReliable(_is);
        int[] record = new int[reqNumber*reqSizeOf];
        for (int i=0; i<record.length; i++){
            record[i]=readReliable(_is);
        }
        Rule aRule = new Rule();
        aRule.hl_digital = new int[Rule.HL_SIZE];
                
        int[] bytes = new int[reqSizeOf];
        int pointer = 0;
        for (int i = 0; i < Rule.HL_SIZE; i++) {
            for (int j = 0; j < bytes.length; j++) {
                bytes[j] = record[pointer*bytes.length+j]; 
            }
            int key = constructInt(bytes);
            aRule.hl_digital[i] = key;
            pointer++;
        }
        for (int j = 0; j < bytes.length; j++) {
            bytes[j] = record[pointer*bytes.length+j]; 
        }
        int key = constructInt(bytes);
        aRule.attrValueDigital = key;
        pointer++;
        if (pointer!=reqNumber) {
            throw new RuntimeException("bytes left");
        }
        
        return aRule;
      }
    
    public int getRuleRecordByteSize() {
        return 3;
    }
    
    public void writePureDigitalRule(final Rule aRule, final OutputStream aStream) throws IOException {
        byte[] bytes = convertRuleToBytes(aRule);
        DataOutputStream dos = new DataOutputStream(aStream);
        dos.write(bytes);
        dos.flush();
    }
    
    public static Rule createFromHLDigitalString(String str, boolean onlyDigital, int ruleNr, int ruleNrGlobal)
    {
        Rule r = new Rule();
        try {
        	//US20 0000640877  0000640877  000000000000509598          N399                                    0   0   2028    0   0   0   0   KTZ_1           20060407103258  A   0       R2  R21 0353    US              R2  R21 US              R2  R21 US  73  31  414 915                     99  730 73079
            char tab = 9; 
            String ts = ""+tab;
            String ts2 = new String(new char[] {tab});
            String[] s = str.split(ts2);
            
            
            if (s[5].trim().equals("")) {
            	int t = 1;
            } 
            if (s[0].trim().equals("")) {
            	int t = 1;
            } 
            if (s.length<46+50) {
            	String[] ss = new String[46+50];
                for (int i = 0; i < ss.length; i++) {
					ss[i] = "";
				}	
                for (int i = 0; i < s.length; i++) {
					ss[i] = s[i].trim();
				}	
                s = ss;
            }
            r.hl_file_values = s;
            int cnt = 0;
            //internalCreateFromHLLevel(r);
            r.hl_digital = new int[Rule.HL_SIZE];
            
            r.ruleNr            = ruleNr;
            r.ruleNrGlobal      = ruleNrGlobal;
            if (!onlyDigital) {
                r.hl = new String[Rule.HL_SIZE];
                Rule.internalCreateFromHLS(r, s);   
            } else {
            }
            Rule.internalCreateFromHLD(r, s);
            
            r.attrValue         = s[5];
            r.attrName          = "/BIC/DUML4";
            r.score = ""+r.getScore();
            r.setOtherAttr(0,-1);
            return r;
        } catch (Exception ex) {
        	System.out.println("Problem with string:");
        	System.out.println(str);
          ex.printStackTrace();   
        }
        return null;
        
    }
    private List readRulesFromCache(IRuleAcceptor ruleAcceptor) {
        List rules = new ArrayList();
    	return null;
    }
  
    
    public List readRulesFromPureDigitalFile(String filename, int maxRecord, IRuleAcceptor ruleAcceptor) {
        try {
        	List rules = new ArrayList();
            
        	if (ruleAcceptor instanceof IRuleAcceptorCache) {
        		IRuleAcceptorCache rcache = (IRuleAcceptorCache)ruleAcceptor;
        		if (rcache.isCacheComplete()) {
        			Set ruleNrs = rcache.getRequiredGlobalRuleNrs();
        			CacheRuleContext cache =  rcache.getCacheRuleContext();
        			for (Iterator iter = ruleNrs.iterator(); iter.hasNext();) {
						Integer globalRuleNr = (Integer) iter.next();
						rules.add(cache.getRule(globalRuleNr));
					}
        			return rules;
        		}
        	}
        	
            InputStream is = new FileInputStream(filename);
            int currentRecord = 0;
            Rule aRule = null;
//            Rule.totalRuleNr = 0;
            boolean eof = false;
            do {
            	
            	int ruleNr = -1;
            	int globalNr = ruleAcceptor.nextRuleGlobalNr();
            	if (ruleAcceptor.acceptGlobalNr(globalNr)) {
                    aRule =readRuleFromPureDigital(is);//,ruleNr,globalNr
                    if (aRule!=null) {
                    	aRule.ruleNrGlobal = globalNr;
                    	if ((ruleAcceptor==null)||((ruleAcceptor!=null)&&(ruleAcceptor.accept(aRule,globalNr)))) {
                            rules.add(aRule);
                            currentRecord ++;
                        }
                    } else {
                    	eof = true;
                    }
            	} else {
            		eof = !scrollToNextRule(is);
            	}
                if (maxRecord>0) {
                    if (currentRecord>=maxRecord) eof = true;
                }
            }while (!eof);
            is.close();         
            
            if (ruleAcceptor instanceof IRuleAcceptorCache) {
        		IRuleAcceptorCache rcache = (IRuleAcceptorCache)ruleAcceptor;
        		CacheRuleContext cache =  rcache.getCacheRuleContext();
    			if (cache!=null) {
            		for (Iterator iter = rules.iterator(); iter.hasNext();) {
    					Rule r = (Rule) iter.next();
    					cache.setRule(r);
    				}
    			}
        	}
            
            return rules;
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
    public List readRulesFromHLFile(String filename, int maxRecord, boolean onlyDigital, IRuleAcceptor ruleAcceptor) {
        try {
            Reader f = new FileReader(filename);
            BufferedReader br = new BufferedReader(f);
            String s = null;
            int currentRecord = 0;
            List rules = new ArrayList();
            do {
                s=br.readLine();
                if (s!=null) {
                    Rule rule = null;
                    rule = createFromHLDigitalString(s,onlyDigital,-1,-1);
                    
                    if ("".equals(rule.attrValue)) {
                        s = null;
                    } else {
                    	int gNr = ruleAcceptor.nextRuleGlobalNr();
                        if ((ruleAcceptor==null)||((ruleAcceptor!=null)&&(ruleAcceptor.accept(rule,gNr)))) {
                            rules.add(rule);
                            currentRecord ++;
                        }
                     }
                }
                if (maxRecord>0) {
                    if (currentRecord>=maxRecord) s = null;
                }
            }while (s!=null);
            f.close();            
            
            return rules;

            
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }
    
    public static Rule createFromHLString(String str, int ruleNr, int ruleNrGlobal)
    {
        Rule r = new Rule();
        try {
        	//US20 0000640877  0000640877  000000000000509598          N399                                    0   0   2028    0   0   0   0   KTZ_1           20060407103258  A   0       R2  R21 0353    US              R2  R21 US              R2  R21 US  73  31  414 915                     99  730 73079
            char tab = 9; 
            String ts = ""+tab;
            String ts2 = new String(new char[] {tab});
            String[] s = str.split(ts2);
            if (s.length<46) {
            	String[] ss = new String[46];
                for (int i = 0; i < ss.length; i++) {
					ss[i] = "";
				}	
                for (int i = 0; i < s.length; i++) {
					ss[i] = s[i];
				}	
                s = ss;
            }
            int cnt = 0;
            r.hl = new String[Rule.HL_SIZE];
           // internalCreateFromHLLevel(r);
            

            r.ruleNr            = ruleNr;
            r.ruleNrGlobal 		= ruleNrGlobal;
     

            Rule.internalCreateFromHLS(r,s);

            
            r.attrName          = "/BIC/DUML4";
            r.attrValue         = s[5];
            
//            r.computeHashCode();
            r.score = ""+r.getScore();
            r.setOtherAttr(0,-1);
            return r;
        } catch (Exception ex) {
        	System.out.println("Problem with string:");
        	System.out.println(str);
          ex.printStackTrace();   
        }
        return null;
        
    }

    
    public static Rule readRuleFromHLFile( String filename, int recordNr ) {
        try {
            Reader f = new FileReader(filename);
            BufferedReader br = new BufferedReader(f);
            String s = null;
            int currentRecord = 0;
            
            do {
                s=br.readLine();
                if (s!=null) {
                    if (currentRecord==recordNr) {
                        Rule rule = createFromHLString( s,0,recordNr );
                        return rule;
                    }
                    currentRecord ++;
                }
            }while (s!=null);
            f.close();            
            
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }
 
    
}
